#!/bin/bash
# Copyright (c) 2017 ~ 2025, the original author wangl.sir individual Inc,
# All rights reserved. Contact us wanglsir<wangl@gmail.com, 983708408@qq.com>
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# @see: https://blogs.wl4g.com/archives/2477
#

# The currently node connection info configuration.
export MGR_MYSQL_BIN=${MGR_MYSQL_BIN:-$(command -v mysql)}
export MGR_MYSQL_PRIMARY_ADDR=${MGR_MYSQL_PRIMARY_ADDR:-'172.8.8.111:3306'}
export MGR_MYSQL_STANDBY_ADDRS=${MGR_MYSQL_STANDBY_ADDRS:-'172.8.8.112:3306,172.8.8.113:3306'}
export MGR_MYSQL_USER=${MGR_MYSQL_USER:-'root'}
export MGR_MYSQL_PASSWORD=${MGR_MYSQL_PASSWORD:-'123456'}
export MGR_MYSQL_LOG_DIR=${MGR_MYSQL_LOG_DIR:-'/var/log/mgrctl'}; mkdir -p ${MGR_MYSQL_LOG_DIR}
export MGR_MYSQL_LOG_FILE="${MGR_MYSQL_LOG_DIR}/mgrctl.log"; touch ${MGR_MYSQL_LOG_FILE} && echo || exit 1
export MGR_MYSQL_REPL_USER=${MGR_MYSQL_REPL_USER:-'repl'}
export MGR_MYSQL_REPL_PASSWORD=${MGR_MYSQL_REPL_PASSWORD:-'123456'}
export MGRCTL_DEBUG=${MGRCTL_DEBUG:-n}
export MGRCTL_VERSION="v2.0.0-20211222"

# -------- MGR members up. --------

function doStartPrimary(){
  local mgrHost=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser=${MGR_MYSQL_USER}
  local mgrPassword=${MGR_MYSQL_PASSWORD}

  loadMGRPlugin "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword"

  # Check elected other primary member?
  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Checking for primary member ..."

  local bootstrapping='ON'
  local primaryAddr=$(getMGRMemberAddrByAll "PRIMARY")
  logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Got MGR primary member address is '$primaryAddr'"
  if [ -n "$primaryAddr" ]; then
    local currentLocalAddr=$(doQuery "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "SELECT gv.VARIABLE_VALUE FROM performance_schema.global_variables gv WHERE gv.VARIABLE_NAME='group_replication_local_address'")
    logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Got current member peer local address is '$currentLocalAddr'"
    # [特别注意]: 如果MGR是运行在docker中的，或者这里从MGR表查询出来的成员Host解析出的IP不能与当前配置的主成员匹配，
    # 则会被认为是已发生重选举，此时启动MGR的参数'group_replication_bootstrap_group'会被错误设置.
    local isSameAddr=$(isSameAddr "$primaryAddr" "$currentLocalAddr")
    if [ "$isSameAddr" != 'Y' ]; then # Has been re-election?
      bootstrapping='OFF'
      logWarn "$mgrUser" "$mgrHost" "$mgrPort" "Cannot startup primary because there is already primary member '$primaryAddr' running.
and it is not the main member of the configured, Re-election has occurred? Attempt to run as an MGR standby member."
    else
      logWarn "$mgrUser" "$mgrHost" "$mgrPort" "The MGR primary member '${MGR_MYSQL_PRIMARY_ADDR}' is running."
    fi
  fi
  logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Set boostrapping is '$bootstrapping'"

  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Starting MGR primary member ..."
  echo "
    SET GLOBAL SUPER_READ_ONLY = OFF;
    SET GLOBAL READ_ONLY = OFF;
    SET SQL_LOG_BIN=0;
    GRANT REPLICATION SLAVE ON *.* TO ${MGR_MYSQL_REPL_USER}@'%'  IDENTIFIED BY '${MGR_MYSQL_REPL_PASSWORD}';
    SET SQL_LOG_BIN=1;
    CHANGE MASTER TO MASTER_USER='${MGR_MYSQL_REPL_USER}', MASTER_PASSWORD='${MGR_MYSQL_REPL_PASSWORD}' FOR CHANNEL 'group_replication_recovery';
    SET GLOBAL group_replication_bootstrap_group=${bootstrapping};
    START GROUP_REPLICATION;
    SET GLOBAL group_replication_bootstrap_group=OFF;
    " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1|grep -v 'insecure'|awk -F ':' '{print $2}'|tee -a ${MGR_MYSQL_LOG_FILE}

  # Check primary member is started?
  checkMGRMemberCount "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "1" "PRIMARY" "ONLINE" "Started MGR primary completed!\n" "Failed to startup MGR primary member.\n"
}

function doStartStandbyAll() {
  # Precheck primary member is online?
  local mgrHost0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser0=${MGR_MYSQL_USER}
  local mgrPassword0=${MGR_MYSQL_PASSWORD}

  # Check primary member is running.
  checkMGRMemberCountByAll "PRIMARY" "ONLINE" "The MGR primary started." "Could not standby members be started because the primary node MGR is not operating normally!"

  # The startup MGR standby all nodes.
  local oldIFS="$IFS"
  IFS="," # Set shell string split separators.
  local standbys=($MGR_MYSQL_STANDBY_ADDRS)
  IFS="$oldIFS"
  for node in ${standbys[@]}; do
    local mgrHost=$(echo $node|awk -F ':' '{print $1}')
    local mgrPort=$(echo $node|awk -F ':' '{print $2}')
    doStartStandby "$mgrHost" "$mgrPort" "$MGR_MYSQL_USER" "$MGR_MYSQL_PASSWORD"
  done
}

function doStartStandby() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4

  loadMGRPlugin "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword"

  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Starting MGR standby member ..."
  echo "
    SET GLOBAL SUPER_READ_ONLY = OFF;
    SET GLOBAL READ_ONLY = OFF;
    SET SQL_LOG_BIN=0;
    GRANT REPLICATION SLAVE ON *.* TO ${MGR_MYSQL_REPL_USER}@'%'  IDENTIFIED BY '${MGR_MYSQL_REPL_PASSWORD}';
    SET SQL_LOG_BIN=1;
    CHANGE MASTER TO MASTER_USER='${MGR_MYSQL_REPL_USER}', MASTER_PASSWORD='${MGR_MYSQL_REPL_PASSWORD}' FOR CHANNEL 'group_replication_recovery';
    SET GLOBAL group_replication_allow_local_disjoint_gtids_join=ON; # High version is outdated.
    SET GLOBAL group_replication_bootstrap_group=OFF;
    START GROUP_REPLICATION;
    SET GLOBAL SUPER_READ_ONLY = ON;
    SET GLOBAL READ_ONLY = ON;
    " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1|grep -v 'insecure'|awk -F ':' '{print $2}'|tee -a ${MGR_MYSQL_LOG_FILE}

  checkMGRMemberCount "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "1" "STANDBY" "ONLINE" "Started MGR standby completed!\n" "Failed to startup MGR standby member.\n"
}

function loadMGRPlugin() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4
  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Checking for MGR plugin ..."

  # Check group replication plugin loaded?
  local isLoaded=$(echo "SELECT COUNT(1) FROM information_schema.PLUGINS p WHERE p.PLUGIN_NAME = 'group_replication' AND p.PLUGIN_STATUS = 'ACTIVE'
  " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>/dev/null | sed -n '2p' | tee -a ${MGR_MYSQL_LOG_FILE})

  if [ "$isLoaded" -gt 0 ]; then
    logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Loaded MGR plugin."
    return 0
  fi

  # Do loading group replication plugin.
  logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Loading MGR plugin ..."

  echo "INSTALL PLUGIN group_replication SONAME 'group_replication.so';
  " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1 | grep -v 'insecure' | sed -r 's/^ERROR/WARNNING/g' | tee -a $MGR_MYSQL_LOG_FILE

  if [ ${PIPESTATUS[0]} -ne 0 ]; then
    logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Loaded MGR plugin completed!"
    return 0
  else
    logErr "$mgrUser" "$mgrHost" "$mgrPort" "Failed to load MGR plugin."
    exit 1
  fi
}

# -------- MGR members down. --------

function doStopMGRPrimary() {
  local mgrHost0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser0=${MGR_MYSQL_USER}
  local mgrPassword0=${MGR_MYSQL_PASSWORD}
  local isForce=$1
  local isReset=$2

  # Check the current standby nodes of the MGR running.
  local onlineStandbyCount=$(getMGRMemberCountByAll "STANDBY")
  if [ "$onlineStandbyCount" -ge 1 ]; then
    logErr "" "" "" "Failed to stopping the MGR primary member, because there are still currently ${onlineStandbyCount} standby members MGR running. Please stop the standby all members first."
    exit 1
  fi

  # Do stopping MGR primary member.
  doStopMGRMember "$mgrHost0" "$mgrPort0" "$mgrUser0" "$mgrPassword0" "$isForce" "PRIMARY" "$isReset"
}

function doStopMGRStandbyAll() {
  local isForce=$1
  local isReset=$2

  # foreach MGR standby all nodes.
  local oldIFS="$IFS"
  IFS="," # Set shell string split separators.
  local standbys=($MGR_MYSQL_STANDBY_ADDRS)
  IFS="$oldIFS"
  for node in ${standbys[@]}; do
    local mgrHost=$(echo $node|awk -F ':' '{print $1}')
    local mgrPort=$(echo $node|awk -F ':' '{print $2}')
    doStopMGRMember "$mgrHost" "$mgrPort" "$MGR_MYSQL_USER" "$MGR_MYSQL_PASSWORD" "$isForce" "STANDBY" "$isReset"
  done
}

function doStopMGRMember() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4
  local isForce=$5
  local mgrNodeRole=$6
  local isReset=$7

  # Checking connection clients all. (SHOW STATUS LIKE '%Threads_connected%')
  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Checking for current of client connections ..."

  local connectedClients=$(echo "
    SELECT
      COUNT(1)
    FROM
      performance_schema.threads t
    WHERE
      t.NAME LIKE '%thread/sql/%'
      AND t.TYPE = 'FOREGROUND'
      AND t.CONNECTION_TYPE LIKE '%TCP%'
      " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>/dev/null | sed -n '2p' | tee -a ${MGR_MYSQL_LOG_FILE})

  if [ "$connectedClients" -gt 0 ]; then
    if [[ -n "$isForce" && "$isForce" -eq 1 ]]; then
      logWarn "$mgrUser" "$mgrHost" "$mgrPort" "[WARNNING] Ignore the current num of client connections is ${connectedClients}."
    else
      logErr "$mgrUser" "$mgrHost" "$mgrPort" "Failed to stopping MGR member, because the current num of client connections is ${connectedClients}. (Refer to: SQL> SHOW STATUS LIKE '%Threads_connected%')"
      exit 1
    fi
  fi

  # Do stopping members.
  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Stopping MGR member ..."

  echo "STOP GROUP_REPLICATION;
  " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1|grep -v 'insecure'|awk -F ':' '{print $2}'|tee -a ${MGR_MYSQL_LOG_FILE}

  # Check MGR member stopped?
  checkMGRMemberCount "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "1" "$mgrNodeRole" "OFFLINE" "Stopped MGR ${mgrNodeRole} completed!" "Failed to stop MGR ${mgrNodeRole} member."

  # Resetting replication.
  if [[ -n "$isReset" && "$isReset" -eq 1 ]]; then
    logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Resetting standby all members with this primary member ..."
    echo "RESET MASTER;RESET SLAVE ALL
    " | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1|grep -v 'insecure'|awk -F ':' '{print $2}'|tee -a ${MGR_MYSQL_LOG_FILE}
  fi
}

# -------- MGR show function. --------

function showMGRMembersByAll() {
  logDebug "" "" "" "Showing MGR member states by All ..."

  local members=""
  # Query by primary node.
  local mgrHost0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser0=${MGR_MYSQL_USER}
  local mgrPassword0=${MGR_MYSQL_PASSWORD}
  local membersByPrimary=$(doShowMGRMembers "$mgrHost0" "$mgrPort0" "$mgrUser0" "$mgrPassword0")
  logDebug "$mgrUser0" "$mgrHost0" "$mgrPort0" "Query members for primary: \n${membersByPrimary}\n"
  members="$membersByPrimary"

  # Query each by standby all nodes.
  local oldIFS="$IFS"
  IFS="," # Set shell string split separators.
  local standbys=($MGR_MYSQL_STANDBY_ADDRS)
  IFS="$oldIFS"
  for node in ${standbys[@]}; do
    local mgrHost=$(echo $node|awk -F ':' '{print $1}')
    local mgrPort=$(echo $node|awk -F ':' '{print $2}')
    local membersByStandby=$(doShowMGRMembers "$mgrHost" "$mgrPort" "$mgrUser0" "$mgrPassword0")
    members=$(echo -e "${members}\n${membersByStandby}")
    logDebug "$mgrUser0" "$mgrHost" "$mgrPort" "Query members for standby: \n${membersByStandby}\n"
  done

  # Merge and de-duplicate
  local summaryMembers=$(echo "$members" | sort -r | uniq)
  if [ -n "$summaryMembers" ]; then
    local header="\t\tGROUP_NAME\t\t\t\tNODE_ID\t\t\tNODE_HOST  NODE_PORT  NODE_STATE NODE_ROLE"
    logInfo "" "" "" "----- Summary MGR All member List. -----\n${header}\n${summaryMembers}"
  else
    logWarn "" "" "" "No data of MGR members found via currently configured primary member. Please check if MGR is started?"
  fi
}

function doShowMGRMembers() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4
  local resultSetText=$(doQuery "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "
  SELECT
    (SELECT gv.VARIABLE_VALUE FROM performance_schema.global_variables gv WHERE gv.VARIABLE_NAME='group_replication_group_name') AS GROUP_NAME,
    rgm.MEMBER_ID AS NODE_ID,
    rgm.MEMBER_HOST AS NODE_HOST,
    rgm.MEMBER_PORT AS NODE_PORT,
    rgm.MEMBER_STATE AS NODE_STATE,
    (CASE (SELECT TRIM(VARIABLE_VALUE) FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member')
      WHEN '' THEN 'UNKOWN'
      WHEN rgm.MEMBER_ID THEN 'PRIMARY'
      ELSE 'STANDBY' END
    ) AS NODE_ROLE
  FROM performance_schema.replication_group_members rgm")

  echo "$resultSetText"
  return 0
}

# ------- MGR checker function. -------

function checkMGRMemberCountByAll() {
  local mgrNodeRole=$1 # Options: PRIMARY|STANDBY
  local expectState=$2 # Options: ONLINE|OFFLINE
  local okmsg=$3
  local errmsg=$4
  logInfo "" "" "" "Checking for MGR member states by All ..."

  local count=$(getMGRMemberCountByAll "$mgrNodeRole")
  logDebug "" "" "" "Got MGR $mgrNodeRole members count is '$count'"                                              

  if [ "$expectState" == 'ONLINE' ]; then
    if [ "$count" -ge 1 ]; then
      logInfo "" "" "" "$okmsg"
    else
      logErr "" "" "" "$errmsg"
      exit 1
    fi
  elif [ "$expectState" == 'OFFLINE' ]; then
    if [ "$count" -le 0 ]; then
      logInfo "" "" "" "$okmsg"
    else
      logErr "" "" "" "$errmsg"
      exit 1
    fi
  fi
}

function checkMGRMemberCount() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4
  local isShowMembers=$5 # Options: 1|0
  local mgrNodeRole=$6 # Options: PRIMARY|STANDBY
  local expectState=$7 # Options: ONLINE|OFFLINE
  local okmsg=$8
  local errmsg=$9

  logInfo "$mgrUser" "$mgrHost" "$mgrPort" "Checking for MGR member states ..."

#   # Show MGR members.
#   if [ "$isShowMembers" -eq 1 ]; then
#     showMGRMembers "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword"
#   fi

  local count=$(getMGRMemberCount "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "$mgrNodeRole")
  logDebug "$mgrUser" "$mgrHost" "$mgrPort" "Got MGR $mgrNodeRole members count is $count by '$mgrUser@$mgrHost:$mgrPort'"

  if [ "$expectState" == 'ONLINE' ]; then
    if [ "$count" -ge 1 ]; then
      logInfo "$mgrUser" "$mgrHost" "$mgrPort" "$okmsg"
    else
      logErr "$mgrUser" "$mgrHost" "$mgrPort" "$errmsg"
      exit 1
    fi
  elif [ "$expectState" == 'OFFLINE' ]; then
    if [ "$count" -le 0 ]; then
      logInfo "$mgrUser" "$mgrHost" "$mgrPort" "$okmsg"
    else
      logErr "$mgrUser" "$mgrHost" "$mgrPort" "$errmsg"
      exit 1
    fi
  fi
}

# ------- MGR member function. -------

function getMGRMemberCountByAll() {
  local roleType=$1 # PRIMARY|STANDBY
  local currentMemberCount=0

  # Query by primary node.
  local mgrHost0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser0=${MGR_MYSQL_USER}
  local mgrPassword0=${MGR_MYSQL_PASSWORD}
  local memberCount=$(getMGRMemberCount "$mgrHost0" "$mgrPort0" "$mgrUser0" "$mgrPassword0" "$roleType")
  if [ "$memberCount" -ge "$currentMemberCount" ]; then
    # Subject to the largest number of running standby nodes found.
    currentMemberCount="$memberCount"
  fi

  # Query each by standby all nodes.
  local oldIFS="$IFS"
  IFS="," # Set shell string split separators.
  local standbys=($MGR_MYSQL_STANDBY_ADDRS)
  IFS="$oldIFS"
  for node in ${standbys[@]}; do
    local mgrHost=$(echo $node|awk -F ':' '{print $1}')
    local mgrPort=$(echo $node|awk -F ':' '{print $2}')
    local memberCount=$(getMGRMemberCount "$mgrHost" "$mgrPort" "$mgrUser0" "$mgrPassword0" "$roleType")
    if [ "$memberCount" -ge "$currentMemberCount" ]; then
      # Subject to the largest number of running members found.
      currentMemberCount="$memberCount"
    fi
  done

  echo $currentMemberCount
  return 0
}

function getMGRMemberCount() {
  local mgrHost=$1
  local mgrPort=$2
  local mgrUser=$3
  local mgrPassword=$4
  local mgrNodeRole=$5
  # Validate arguments.
  if [[ "$mgrNodeRole" != 'PRIMARY' && "$mgrNodeRole" != 'STANDBY' ]]; then
    logErr "Bad arguments, invalid arg mgrNodeRole for '${mgrNodeRole}'!"
    exit 1
  fi

  # Query MGR members count by role.
  local count=$(doQuery "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "
      SELECT COUNT(1) FROM (SELECT
        (CASE (SELECT TRIM(VARIABLE_VALUE) FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member')
          WHEN '' THEN 'UNKOWN'
          WHEN rgm.MEMBER_ID THEN 'PRIMARY'
          ELSE 'STANDBY' END
        ) AS NODE_ROLE
      FROM performance_schema.replication_group_members rgm) alias WHERE alias.NODE_ROLE = '${mgrNodeRole}'
      ")
  echo "$count"
  return 0
}

function getMGRMemberAddrByAll() {
  local roleType=$1 # PRIMARY|STANDBY
  # Query by primary node.
  local mgrHost0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $1}')
  local mgrPort0=$(echo $MGR_MYSQL_PRIMARY_ADDR|awk -F ':' '{print $2}')
  local mgrUser0=${MGR_MYSQL_USER}
  local mgrPassword0=${MGR_MYSQL_PASSWORD}
  local addr=$(getMGRMemberAddr "$mgrHost0" "$mgrPort0" "$mgrUser0" "$mgrPassword0" "$roleType")
  if [ -n "$addr" ]; then
    echo "$addr"
    return 0
  fi
 
  # Query each by standby all nodes.
  local oldIFS="$IFS"
  IFS="," # Set shell string split separators.
  local standbys=($MGR_MYSQL_STANDBY_ADDRS)
  IFS="$oldIFS"
  for node in ${standbys[@]}; do
    local mgrHost=$(echo $node|awk -F ':' '{print $1}')
    local mgrPort=$(echo $node|awk -F ':' '{print $2}')
    local addr=$(getMGRMemberAddr "$mgrHost" "$mgrPort" "$mgrUser0" "$mgrPassword0" "$roleType")
    if [ -n "$addr" ]; then
      echo "$addr"
      return 0
    fi
  done
}

function getMGRMemberAddr() {
  local mgrHost=$1    
  local mgrPort=$2    
  local mgrUser=$3    
  local mgrPassword=$4
  local roleType=$5 # PRIMARY|STANDBY
  local memberAddr=$(doQuery "$mgrHost" "$mgrPort" "$mgrUser" "$mgrPassword" "
    SELECT CONCAT(alias.MEMBER_HOST, ':', alias.MEMBER_PORT) FROM (SELECT
      rgm.MEMBER_HOST,
      rgm.MEMBER_PORT,
      (CASE (SELECT TRIM(VARIABLE_VALUE) FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member')
        WHEN '' THEN 'UNKOWN'
        WHEN rgm.MEMBER_ID THEN 'PRIMARY'
        ELSE 'STANDBY' END
      ) AS NODE_ROLE  
    FROM performance_schema.replication_group_members rgm) alias WHERE alias.NODE_ROLE='${roleType}'
  ")
  echo "$memberAddr"
  return 0
}

# ------- MGR tools function. -------

function doQuery() {
  local mgrHost=$1    
  local mgrPort=$2    
  local mgrUser=$3    
  local mgrPassword=$4
  local mgrSql=$5
  local resultSetText=$(echo $mgrSql | $MGR_MYSQL_BIN -h${mgrHost} -P${mgrPort} -u${mgrUser} -p${mgrPassword} 2>&1 | grep -v 'insecure' | sed '1d')
  echo "$resultSetText"
  return 0
}

function isSameAddr() {
  # Remove the port part.
  local addr1=$(echo $1 | awk -F ':' '{print $1}')
  local addr2=$(echo $2 | awk -F ':' '{print $1}')
  local ip1=$(ping $addr1 -c 1 -w 1 2>/dev/null | sed '1{s/[^(]*(//;s/).*//;q}')
  local ip2=$(ping $addr2 -c 1 -w 1 2>/dev/null | sed '1{s/[^(]*(//;s/).*//;q}')
  [ "$ip1" == "$ip2" ] && echo "Y" || echo "N"
}

function preflightCheck() {
  # Check local mysql version.
  if [ -z "$MGR_MYSQL_BIN" ]; then
    logErr "" "" "" "Please install the mysql command-line client."
    exit 1
  fi
  local mysqlVer="$($MGR_MYSQL_BIN --version)"
  if [ -z "$(echo $mysqlVer|grep '5.7.')" ]; then
    logErr "" "" "" "Unsupported mysql client version for '$mysqlVer'."
    exit 1
  fi
}

# -------- Logging function. --------

function log() {
  local logLevel=$1
  local mgrUser=$2
  local mgrHost=$3
  local mgrPort=$4
  local logMsg=$5
  if [[ "$mgrUser" != '' && "$mgrHost" != '' && "$mgrPort" != '' ]]; then
    local logMsg="[$logLevel] $(date '+%y-%m-%d %H:%M:%S') - [${mgrUser}@${mgrHost}:${mgrPort}] - $logMsg"
  else
    local logMsg="[$logLevel] $(date '+%y-%m-%d %H:%M:%S') - $logMsg"
  fi
  echo -e "${logMsg}" | tee -a $MGR_MYSQL_LOG_FILE
}

function logDebug() {
  local debug=$(echo $MGRCTL_DEBUG | tr '[a-z]' '[A-Z]')
  if [[ "$debug" == 'Y' || "$debug" == 'YES' || "$debug" == 'ON' || "$debug" == 'TRUE' ]]; then
    log "\033[37mDEBUG\033[0m" "$@"
  fi
}

function logInfo() {
  log "\033[32mINFO\033[0m " "$@"
}

function logWarn() {
  log "\033[33mWARN\033[0m " "$@"
}

function logErr() {
  log "\033[31mERROR\033[0m" "$@"
}

function printHelp() {
  logInfo "" "" "" "The MGR(MySQL Group Replication) lightweight management CLI - $MGRCTL_VERSION
 __ __            ___  ___  _
|  \  \ ___  _ _ |  _>|_ _|| |
|     |/ . || '_>| <__ | | | |_
|_|_|_|\_. ||_|  \___/ |_| |___|
       <___|

Time    : $(date +'%Y-%m-%d %H:%M:%S')
Git     : https://gitee.com/wl4g/blogs/raw/master/docs/articles/database/docker-mysql-mgr-simple-deployment/resources/mgrctl.sh
Authors : <wanglsir@gmail.com, 983708408@qq.com>
Log     : ${MGR_MYSQL_LOG_FILE}

Usage: ./$(basename $0) [COMMAND] [opt1] [opt2] ...
  up                                         Booting the MGR node members to the running state.
    -p,--primary                             For booting MGR primary node. (Notice: Currently only supports single primary mode)
    -s,--standbys                            For booting MGR all standby nodes. (Notice: Please ensure that it must be executed 
                                              after the MGR primary is started normally, otherwise it will cause the primary node
                                              to boot into confusion)
    -a,--all                                 For bootstrapping MGR single primary and all standby nodes.

  down                                       Booting the MGR node members to the stopped state. (Notice: All client connections must
                                              be disconnected before stopping the MGR component, otherwise data may be inconsistent 
                                              during the stopping process, or use '--force' to force a stop. It is not recommended to 
                                              do so as a last resort)
    -p,--primary  [-f,--force]               For stop MGR primary node.
                               [-r,--reset]  For execute 'RESET SLAVE ALL' before booting MGR. (WARNNING: In MySQL 5.7 will modify cleanup
                                              all its slave repplier information, including primary member address, port, user password, 
                                              gtid_executed, gtid_purged, etc. Please make sure that you are completely understand the behavior
                                              of this action. Similer MHA will execute 'RESET SLAVE/*!50516 ALL*/' on the new primary member 
                                              during failover to clear all slave member information.)
    -s,--standbys                            For stop MGR standbys all nodes.
                  [-f,--force]
                               [-r,--reset]  For execute 'RESET MASTER' before booting MGR. (WARNNING: In MySQL 5.7 will cleanup the information
                                               with the primary member, including primary member address, port, user password, gtid_executed, 
                                               gtid_purged, etc. Please make sure that you are completely understand the behavior of this action.)
    -a,--all      [-f,--force]               For stop MGR primary and standbys all nodes.

  members                                    Print MGR member states. the default it be connect to the each all members for summary query.
"
}

function main() {
  preflightCheck

case $1 in
  version|-version|--version|v|-v)
    printVersion
    ;;
  up)
    case $2 in
      --primary|-p)
        doStartPrimary
        showMGRMembersByAll
        ;;
      --standbys|-s)
        doStartStandbyAll
        showMGRMembersByAll
        ;;
      --all|-a)
        doStartPrimary
        doStartStandbyAll
        showMGRMembersByAll
        ;;
      --help|-h|*)
        printHelp
        ;;
    esac
    ;;
  down)
    local isForce=$([[ -n $3 && ("$3" -eq '--force' || "$3" -eq '-f') ]] && echo '1' || echo '0')
    local isReset=$([[ -n $4 && ("$4" -eq '--reset' || "$4" -eq '-r') ]] && echo '1' || echo '0')
    case $2 in
      --primary|-p)
        doStopMGRPrimary "$isForce" "$isReset"
        showMGRMembersByAll
        ;;
      --standbys|-s)
        doStopMGRStandbyAll "$isForce" "$isReset"
        showMGRMembersByAll
        ;;
      --all|-a)
        doStopMGRStandbyAll "$isForce" "$isReset"
        doStopMGRPrimary "$isForce" "$isReset"
        showMGRMembersByAll
        ;;
      *)
        printHelp
        ;;
    esac
    ;;
  members)
    case $2 in
      help|--help|-h)
        printHelp
        ;;
      *)
        showMGRMembersByAll
        ;;
    esac
    ;;
  help|--help|-h|*)
    printHelp
    exit 1
esac
}

# --- Main. ---
main "$@"